﻿using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using OfficeOpenXml;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Reflection;
using UnityEngine;

public static class ExcelToolCore
{
    public const char SUBCLASS_SEPARATOR = '@';
    public const char ARRAY_SEPARATOR = ',';
    public const char RANGE_SEPARATOR = '-';
    public static string[] boolNameList = new string[] { "FALSE", "TRUE" };

    /// <summary>
    /// 支持生成嵌套的Class属性(仅支持单层嵌套）
    /// </summary>
    public static void GenerateExcelTemplate(Type type, string excelSaveDirectory)
    {
        var configuableAttr = type.GetCustomAttribute<ConfiguableAttribute>();
        bool subclassFieldFirst = configuableAttr.subclassFieldFirst;
        var fieldInfos = GetFieldInfos(type, subclassFieldFirst);
        using (var package = new ExcelPackage())
        {
            try
            {
                List<string> sheetNames = null;
                string[] enumNames = null;
                var strType = typeof(string);
                List<ExcelWorksheet> worksheets = new List<ExcelWorksheet>();
                if (sheetNames != null)
                {
                    foreach (var sheetName in sheetNames)
                    {
                        worksheets.Add(package.Workbook.Worksheets.Add(sheetName));
                    }
                }
                else
                {
                    string sheetName = configuableAttr == null ? type.Name : configuableAttr.excelSheetName;
                    if (string.IsNullOrEmpty(sheetName))
                    {
                        sheetName = type.Name;
                    }
                    worksheets.Add(package.Workbook.Worksheets.Add(sheetName));
                }
                Queue<FieldInfo> subclassFieldInfos = new Queue<FieldInfo>();
                bool isSubclassProperty = false;
                string subclassName, subclassDesc;
                int subPropertyIndex = 0;
                for (int j = 0; j < worksheets.Count; j++)
                {
                    var worksheet = worksheets[j];
                    worksheet.DefaultColWidth = 20;
                    int freezeCol = 1;
                    for (int i = 0; i < fieldInfos.Count; i++)
                    {
                        var fieldInfo = fieldInfos[i];
                        var fieldType = fieldInfo.FieldType;
                        //对Class进行单独解析
                        if (fieldType.IsClass && fieldType != strType)
                        {
                            subclassFieldInfos = new Queue<FieldInfo>(GetFieldInfos(fieldType, subclassFieldFirst));
                            subclassName = fieldInfo.Name;
                            subclassDesc = fieldInfo.GetCustomAttribute<DescriptionAttribute>()?.Description;
                        }
                        else
                        {
                            subclassDesc = string.Empty;
                            subclassName = string.Empty;
                        }
                    ProcessSubclass:
                        if (subclassFieldInfos.Count > 0)
                        {
                            fieldInfo = subclassFieldInfos.Dequeue();
                            isSubclassProperty = true;
                        }
                        else
                        {
                            isSubclassProperty = false;
                        }
                        string desc = fieldInfo.GetCustomAttribute<DescriptionAttribute>()?.Description;
                        if (isSubclassProperty)
                        {
                            desc = $"{subclassDesc}{SUBCLASS_SEPARATOR}{desc}";
                        }
                        int index = i + 1 + subPropertyIndex;
                        var nameCell = worksheet.Cells[1, index];
                        string name = isSubclassProperty ? $"{subclassName}{SUBCLASS_SEPARATOR}{fieldInfo.Name}" : fieldInfo.Name;
                        nameCell.Value = name;
                        nameCell.Style.HorizontalAlignment = OfficeOpenXml.Style.ExcelHorizontalAlignment.Center;
                        nameCell.Style.Border.BorderAround(OfficeOpenXml.Style.ExcelBorderStyle.Thin);
                        var descCell = worksheet.Cells[2, index];
                        descCell.Style.Font.Bold = true;
                        descCell.Style.HorizontalAlignment = OfficeOpenXml.Style.ExcelHorizontalAlignment.Center;
                        descCell.Style.Border.BorderAround(OfficeOpenXml.Style.ExcelBorderStyle.Thin);
                        descCell.Value = desc;
                        if (fieldInfo.GetCustomAttribute<ExcelFreezeAttribute>() != null)
                        {
                            freezeCol = index + 1;
                        }
                        if (fieldType.IsEnum)
                        {
                            var validation = worksheet.DataValidations.AddListValidation(worksheet.Cells[3, index, worksheet.Cells.Rows, index].Address);
                            enumNames = Enum.GetNames(fieldType);
                            validation.Formula.ExcelFormula = string.Empty;
                            try
                            {
                                int lengthCount = 0;
                                foreach (var enumName in enumNames)
                                {
                                    validation.Formula.Values.Add(enumName);
                                    lengthCount += enumName.Length;
                                }
                            }
                            catch (Exception ex)
                            {
                                //InvalidOperationException: The total length of a DataValidation list cannot exceed 255 characters
                                if (ex is InvalidOperationException)
                                {
                                    worksheet.DataValidations.Remove(validation);
                                }
                            }
                        }
                        if (fieldType == typeof(bool))
                        {
                            var validation = worksheet.DataValidations.AddListValidation(worksheet.Cells[3, index, worksheet.Cells.Rows, index].Address);
                            enumNames = boolNameList;
                            validation.Formula.ExcelFormula = string.Empty;
                            int lengthCount = 0;
                            foreach (var enumName in enumNames)
                            {
                                validation.Formula.Values.Add(enumName);
                                lengthCount += enumName.Length;
                            }
                        }
                        worksheet.Column(index).AutoFit();
                        if (subclassFieldInfos.Count > 0)
                        {
                            subPropertyIndex++;
                            goto ProcessSubclass;
                        }
                    }
                    //冻结行和列
                    worksheet.View.FreezePanes(3, freezeCol);
                }
                string fileName = string.IsNullOrEmpty(configuableAttr.fileName) ? type.Name : configuableAttr.fileName;
                string filePath = Path.Combine(excelSaveDirectory, $"{fileName}.xlsx");
                package.SaveAs(new FileInfo(filePath));
                Debug.Log($"[{type}] 配置表生成成功，文件路径：{filePath}");
            }
            catch (Exception ex)
            {
                Debug.LogException(ex);
            }
        }
    }

    private static List<FieldInfo> GetFieldInfos(Type type, bool subclassFieldFirst = false)
    {
        List<FieldInfo> fieldInfos = new List<FieldInfo>();
        Type tempType = type;
        List<Type> allTypes = new List<Type>();
        while (tempType != typeof(object))
        {
            allTypes.Insert(0, tempType);
            tempType = tempType.BaseType;
        }
        if (subclassFieldFirst)
        {
            allTypes.Reverse();
        }
        for (int i = 0; i < allTypes.Count; i++)
        {
            fieldInfos.AddRange(allTypes[i].GetFields(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly));
        }
        //忽略带有ConfigIgnoreAttribute的属性
        fieldInfos.RemoveAll(fi => fi.GetCustomAttribute<ConfigIgnoreAttribute>() != null);
        return fieldInfos;
    }


    public static bool GenerateJsonFile(Type configuableType, string excelFilePath, string jsonFilePath, bool prettyFormat = false)
    {
        if (!File.Exists(excelFilePath))
        {
            Debug.LogError($"[{excelFilePath}]文件不存在");
            return false;
        }
        var fileStream = new FileStream(excelFilePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
        using (var package = new ExcelPackage(fileStream))
        {
            try
            {
                JArray jArray = new JArray();
                foreach (var sheet in package.Workbook.Worksheets)
                {
                    if (sheet.Name.ToLower().Contains("ignore"))
                        continue;
                    var rowCount = sheet.Dimension.End.Row;
                    var columnCount = sheet.Dimension.End.Column;
                    var cells = sheet.Cells;
                    List<string> titles = new List<string>();
                    for (int i = 1; i <= columnCount; i++)
                    {
                        var cell = cells[1, i];
                        if (cell != null && !string.IsNullOrEmpty((string)cell.Value))
                        {
                            titles.Add(cell.Value.ToString());
                        }
                        else
                        {
                            titles.Add(string.Empty);
                        }
                    }
                    for (int i = 3; i <= rowCount; i++)
                    {
                        var jObject = new JObject();
                        JObject currentJObject = null;
                        for (int j = 1; j <= columnCount; j++)
                        {
                            var value = cells[i, j].Value;
                            if (value == null)
                                continue;
                            var title = titles[j - 1];
                            if (string.IsNullOrEmpty(title))
                                continue;
                            if (configuableType != null)
                            {
                                var fieldName = title;
                                if (fieldName.Contains(SUBCLASS_SEPARATOR.ToString()))
                                {
                                    fieldName = title.Split(SUBCLASS_SEPARATOR)[0];
                                }
                            }
                            if (title.Contains(SUBCLASS_SEPARATOR.ToString()))
                            {
                                var strs = title.Split(SUBCLASS_SEPARATOR);
                                var subTitle = strs[0];
                                title = strs[1];
                                if (jObject.GetValue(subTitle) == null)
                                {
                                    jObject[subTitle] = new JObject();
                                }
                                currentJObject = jObject[subTitle] as JObject;
                            }
                            else
                            {
                                currentJObject = jObject;
                            }
                            //跳过默认值
                            if (value.GetType().IsValueType && long.TryParse(value.ToString(), out long longV1))
                            {
                                currentJObject.Add(title, longV1);
                            }
                            else
                            {
                                var str = value.ToString();
                                //数组处理
                                if (str.StartsWith("[") && str.EndsWith("]"))
                                {
                                    currentJObject.Add(title, JArray.Parse(str));
                                }
                                //object处理
                                else if (str.StartsWith("{") && str.EndsWith("}"))
                                {
                                    currentJObject.Add(title, JObject.Parse(str));
                                }
                                else
                                {
                                    Type enumType = null;
                                    if (configuableType != null)
                                    {
                                        var fieldInfo = configuableType.GetField(title);
                                        if (fieldInfo != null)
                                        {
                                            enumType = fieldInfo.FieldType;
                                        }
                                    }
                                    //枚举处理
                                    if (enumType.IsEnum)
                                    {
                                        if (enumType.GetCustomAttribute<FlagsAttribute>() != null)
                                        {
                                            var enumStrs = str.Split(',');
                                            var enumInt = 0;
                                            for (int k = 0; k < enumStrs.Length; k++)
                                            {
                                                enumInt = enumInt | (int)Enum.Parse(enumType, enumStrs[k]);
                                            }
                                            currentJObject.Add(title, enumInt);
                                        }
                                        else
                                        {
                                            currentJObject.Add(title, (int)Enum.Parse(enumType, str));
                                        }
                                    }
                                    else
                                    {
                                        currentJObject.Add(title, JToken.FromObject(value));
                                    }
                                }
                            }
                        }
                        if (jObject.Count > 0)
                        {
                            jArray.Add(jObject);
                        }
                        else
                        {
                            Debug.LogWarning($"[{configuableType}]配置文件存在空行，行号：{i}");
                        }
                    }
                }
                File.WriteAllText(jsonFilePath, jArray.ToString(prettyFormat ? Formatting.Indented : Formatting.None));
                Debug.Log($"[{configuableType}] Json文件生成成功，文件路径：{jsonFilePath}");
            }
            catch (Exception ex)
            {
                Debug.LogError($"[{configuableType}] Json文件生成失败");
                Debug.LogException(ex);
            }
            fileStream.Close();
        }
        return true;
    }
}